#include "stdafx.h"
#include <fstream>

static const std::string sError = "error";

HSLevel* HSLevel::sLevel = NULL;

HSLevel& HSLevel::GetInstance()
{
	if ( !sLevel )
	{
		sLevel = new HSLevel();
	}

	return *sLevel;
}

HSTileActor::HSTileActor()
{
	pTile = NULL;
}

bool HSTileActor::Create( const HSTile* pT, float fpX, float fpY )
{
	if ( !pT )
	{
		return false;
	}

	float fPixelToWorld = MathUtil::PixelsToWorldUnits( 1.0f );

	SetSize( (float)pT->uiSizeX * fPixelToWorld, (float)pT->uiSizeY * fPixelToWorld );
	SetPosition( fpX * fPixelToWorld, fpY * fPixelToWorld );
	
	if ( !SetSprite( "Resources/Images/" + pT->sTex ) )
	{
		return false;
	}

	pTile = pT;
	return true;
}

const std::string& HSTileActor::GetTileId() const
{
	if ( pTile )
	{
		return pTile->sId;
	}

	return sError;
}

HSObjectActor::HSObjectActor()
{
	pObj = NULL;
	iObjLayer = 10000;
}

bool HSObjectActor::Create( const HSObject* pO, float fpX, float fpY )
{
	if ( !pO )
	{
		return false;
	}

	float fPixelToWorld = MathUtil::PixelsToWorldUnits( 1.0f );

	SetSize( (float)pO->uiSizeX * fPixelToWorld, (float)pO->uiSizeY * fPixelToWorld );
			
	if ( pO->uiFrameCount > 1 )
	{
		LoadSpriteFrames( "Resources/Images/" + pO->sTex, GL_CLAMP, GL_LINEAR );
		PlaySpriteAnimation( pO->fAnimDelay, SAT_Loop, 0, pO->uiFrameCount );
	}
	else
	{
		if ( !SetSprite( "Resources/Images/" + pO->sTex, 0, GL_CLAMP, GL_LINEAR, false ) )
		{
			return false;
		}
	}
			
	SetPosition( fpX * fPixelToWorld, (fpY + (pO->uiSizeY / 2.0f)) * fPixelToWorld );
	iObjLayer = -( fpY / 32.0f );
	pObj = pO;
	return true;
}

void HSObjectActor::SetObjectLayer()
{
	theWorld.UpdateLayer( this, iObjLayer );
}

const std::string& HSObjectActor::GetActionType()
{
	if ( pObj )
	{
		return pObj->sActionType;
	}

	return sError;
}

const std::string& HSObjectActor::GetActionArg()
{
	if ( pObj )
	{
		return pObj->sActionArg;
	}

	return sError;
}

const std::string& HSObjectActor::GetObjectId() const
{
	if ( pObj )
	{
		return pObj->sId;
	}

	return sError;
}

HSLevel::HSLevel()
{
	bLoaded = false;
	bActive = false;
	fWorldToPixel = 0.0f;
	fPixelToWorld = 0.0f;
}

HSLevel::~HSLevel()
{
	Unload();
}

void HSLevel::MouseMotionEvent(Vec2i screenCoordinates)
{
	if ( !bActive )
	{
		return;
	}

	return;
}

void HSLevel::MouseDownEvent(Vec2i screenCoordinates, MouseButtonInput button)
{
	if ( !bActive )
	{
		return;
	}

	return;
}

void HSLevel::MouseUpEvent(Vec2i screenCoordinates, MouseButtonInput button)
{
	if ( !bActive )
	{
		return;
	}

	if ( button == MOUSE_LEFT )
	{
		HSPlayer::GetInstance().GoTo( screenCoordinates );
		return;
	}
	
	if ( button == MOUSE_RIGHT )
	{
		Vector2 wCoords = MathUtil::ScreenToWorld( screenCoordinates );

		for ( unsigned int i = 0; i < vctObjects.size(); i++ )
		{
			float fDistSquared = Vector2::DistanceSquared( HSPlayer::GetInstance().GetPosition(), vctObjects[i]->GetPosition() );
			
			if ( vctObjects[i]->GetBoundingBox().Contains( wCoords ) &&
				 fDistSquared < MathUtil::PixelsToWorldUnits( 256.0 ) )
			{
				if ( vctObjects[i]->GetActionType() == "dialog" )
				{
					Deactivate();
					HSPlayer::GetInstance().Deactivate();
					HSDialog::GetInstance().ShowDialog( vctObjects[i]->GetActionArg() );
					HSDialog::GetInstance().Activate();
					return;
				}
			}
		}

		return;
	}

	if ( button == MOUSE_MIDDLE )
	{
		Deactivate();
		thePlayer.Deactivate();
		theInv.Activate();
		return;
	}
}

void HSLevel::MouseWheelEvent(const Vector2 &scrollOffset)
{
	if ( !bActive )
	{
		return;
	}

	return;
}

bool HSLevel::Load( const std::string& sLevelFile )
{
	if ( bLoaded )
	{
		return false;
	}

	std::ifstream in( "Resources/Data/Maps/" + sLevelFile, std::ifstream::in );

	if ( !in.is_open() )
	{
		sysLog.Printf( "Failed to open %s", sLevelFile.c_str() );
		return false;
	}
	
	fPixelToWorld = MathUtil::PixelsToWorldUnits( 1.0f );
	fWorldToPixel = MathUtil::WorldUnitsToPixels( 1.0f );

	while ( !in.eof() )
	{
		if ( in.fail() )
		{
			sysLog.Printf( "Failed to parse data from %s", sLevelFile.c_str() );
			return false;
		}
		
		std::string sType, sId;
		float fPixelPosX, fPixelPosY;
		in >> sType >> sId >> fPixelPosX >> fPixelPosY;
				
		if ( sType == "ground" )
		{
			HSTileActor* pNewTileActor = new HSTileActor();

			if ( !pNewTileActor )
			{
				sysLog.Log( "[HSLevel::Load] Out of memory" );
				return false;
			}

			if ( !pNewTileActor->Create( GetTile( sId ), fPixelPosX, fPixelPosY ) )
			{
				sysLog.Printf( "[HSLevel::Load] Failed to create tile '%s'", sId.c_str() );
				return false;
			}

			vctGround.push_back( pNewTileActor );
		}
		else if ( sType == "object" )
		{
			HSObjectActor* pNewObjectActor = new HSObjectActor();

			if ( !pNewObjectActor )
			{
				sysLog.Log( "[HSLevel::Load] Out of memory" );
				return false;
			}

			if ( !pNewObjectActor->Create( GetObject( sId ), fPixelPosX, fPixelPosY ) )
			{
				sysLog.Printf( "[HSLevel::Load] Failed to create object '%s'", sId.c_str() );
				return false;
			}

			vctObjects.push_back( pNewObjectActor );
		}
		else
		{
			sysLog.Printf( "Unrecognized data type %s in %s", sType.c_str(), sLevelFile.c_str() );
			return false;
		}

	}
		
	bLoaded = true;
	return true;
}

void HSLevel::Unload()
{
	for ( size_t i = 0; i < vctGround.size(); i++ )
	{
		theWorld.Remove( vctGround[i] );
		delete vctGround[i];
	}

	for ( size_t i = 0; i < vctObjects.size(); i++ )
	{
		theWorld.Remove( vctObjects[i] );
		delete vctObjects[i];
	}

	bLoaded = false;
}

void HSLevel::Activate()
{
	if ( bActive )
	{
		return;
	}

	for ( size_t i = 0; i < vctGround.size(); i++ )
	{
		theWorld.Add( vctGround[i], HSTileActor::iGroundLevel );
	}

	for ( size_t i = 0; i < vctObjects.size(); i++ )
	{
		theWorld.Add( vctObjects[i], vctObjects[i]->iObjLayer );
	}

	bActive = true;
}

void HSLevel::Deactivate()
{
	if ( !bActive )
	{
		return;
	}

	for ( size_t i = 0; i < vctGround.size(); i++ )
	{
		theWorld.Remove( vctGround[i] );
	}

	for ( size_t i = 0; i < vctObjects.size(); i++ )
	{
		theWorld.Remove( vctObjects[i] );
	}

	bActive = false;
}
